#!/usr/bin/env python3
import os
import sys
import _io

def repoint(rom:_io.BufferedReader, space:int, repoint_at, slidefactor=0):
	rom.seek(repoint_at)
	space += slidefactor
	data = (space.to_bytes(4, 'little'))
	rom.write(bytes(data))

def replacebytes(rom:_io.BufferedReader, address:int, data:list[str])->None:
	rom.seek(address, os.SEEK_SET)
	rom.write(bytes([int(byte, 16) for byte in data]))

def hook(rom:_io.BufferedReader, space:int, hook_at, register=0)->None:
	if hook_at & 1:
		hook_at -= 1
	rom.seek(hook_at)
	register &= 7
	if hook_at % 4:
		data = bytes([0x01, 0x48 | register, 0x00 | (register << 3), 0x47, 0x0, 0x0])
	else:
		data = bytes([0x00, 0x48 | register, 0x00 | (register << 3), 0x47])
	space |= 0x1
	data += (space.to_bytes(4, 'little'))
	rom.write(bytes(data))

def insert(rom:_io.BufferedReader, offset:int)->None:
	rom.seek(offset, os.SEEK_SET)
	with open(f"{sys.argv[4]}", "rb") as binary:
		data = binary.read()
	rom.write(data)

def symbols(offset:int)->dict[str, int]:
	ret = {}
	with open(f"{sys.argv[3]}", "r") as symfile:
		lines = symfile.readlines()
	for line in lines:
		parts = line.strip().split()
		symoffset, symname = int(parts[0], 16), parts[1]
		ret[symname] = symoffset - offset
	return ret

def main():
	offset = int(sys.argv[1], 16)
	with open(f"{sys.argv[2]}", "rb+") as rom:
		table = symbols(offset)
		insert(rom, offset)
		for entry in table:
			table[entry] += offset

		with open("clears", "r") as clearlist:
			lines = clearlist.readlines()
		for line in lines:
			if line.strip() == "" or line.startswith("#"): continue
			address, fillbyte, size = line.strip().split()
			address, size = int(address, 16) - 0x08000000, int(size, 16)
			data = bytes([int(fillbyte, 16)] * size)
			rom.seek(address)
			rom.write(data)

		with open("hooks", "r") as hooklist:
			lines = hooklist.readlines()
		for line in lines:
			if line.strip() == "" or line.startswith("#"): continue
			symbol, address, register = line.strip().split()
			address = int(address, 16) - 0x08000000
			try:
				code = table[symbol]
			except KeyError:
				print(f"Symbol {symbol} not found.")
				continue
			hook(rom, code, address, int(register))

		with open("bytereplacement", "r") as replacelist:
			lines = replacelist.readlines()
		for line in lines:
			if line.strip() == "" or line.startswith("#"): continue
			source = line.strip().split()
			address, data = int(source[0], 16) - 0x08000000, source[1:]
			replacebytes(rom, address, data)

		with open("repoints", "r", -1, encoding="utf-8") as repointlist:
			lines = repointlist.readlines()
		for line in lines:
			if line.startswith("#"): continue
			if len(line.strip().split()) == 2:
				symbol, address = line.strip().split()[0], int(line.strip().split()[1], 16) - 0x08000000
				try:
					code = table[symbol]
				except KeyError:
					print('Symbol missing:', symbol)
					continue
				repoint(rom, code, address)
			elif len(line.strip().split()) == 3:
				symbol, address, slidefactor = line.strip().split()
				address = int(address, 16) - 0x08000000
				try:
					code = table[symbol]
				except KeyError:
					print('Symbol missing:', symbol)
					continue
				repoint(rom, code, address, int(slidefactor))

		with open('routinepointers', 'r') as pointerlist:
			for line in pointerlist:
				if line.strip().startswith('#'): continue
				symbol, address = line.split()
				offset = int(address, 16) - 0x08000000
				try:
					code = table[symbol]
				except KeyError:
					print('Symbol missing:', symbol)
					continue
				repoint(rom, code, offset, 1)


if __name__ == "__main__":
	if sys.version_info < (3, 8):
		print("Python 3.8 or later is required.")
		sys.exit(1)
	else:
		main()